package com.commons.dbutils.util;

import java.io.IOException;

import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLHandshakeException;

import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.StatusLine;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;


public class HttpClientUtil {
	private static final Logger log = Logger.getLogger(HttpClientUtil.class);
	private static final String CHARSET = Consts.UTF_8.displayName();
	public  static final PoolingClientConnectionManager CONMAN;
	private static DefaultHttpClient HTTPCLIENT;
	private List<Header> headers;
	private HttpContext localContext;
	//设置连接最大数  
	private static final int MAX_TOTAL=1;
	//设置每个Route的连接最大数  
	private static final int MAX_PER_ROUTE=1;
	private static final int OK=200;
	/**
	 * 连接超时时间,默认20秒
	 */
	private int connectionTimeOut = 20000;
	/**
	 * 读数据超时时间,默认30秒
	 */
	private int socketTimeOut = 30000;
	
	/**
	 * 自定义HTTP状态-连接超时
	 */
	public static final int STATUS_CODE_TIMEOUT = 600;
	/**
	 * 自定义HTT状态-其它错误
	 */
	public static final int STATUS_CODE_OTHER = 603;
	
	public HttpClientUtil() {
		localContext = new BasicHttpContext();
	}

	static {

		HttpParams params = new BasicHttpParams();
		// 设置一些基本参数
		HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
		HttpProtocolParams.setContentCharset(params, CHARSET);
		HttpProtocolParams.setUseExpectContinue(params, true);
		HttpClientParams.setCookiePolicy(params, CookiePolicy.IGNORE_COOKIES);
		HttpProtocolParams
				.setUserAgent(
						params,"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16");
		
		// 设置我们的HttpClient支持HTTP和HTTPS两种模式
		SchemeRegistry schReg = new SchemeRegistry();
		schReg.register(new Scheme("http",  80 ,PlainSocketFactory.getSocketFactory()));
		schReg.register(new Scheme("https", 443,SSLSocketFactory.getSocketFactory()));

		CONMAN = new PoolingClientConnectionManager(schReg);
		//设置每个Route的连接最大数  
		CONMAN.setDefaultMaxPerRoute(MAX_PER_ROUTE);
		//设置连接最大数  
		CONMAN.setMaxTotal(MAX_TOTAL);
		
		HTTPCLIENT = new DefaultHttpClient(CONMAN, params);
		//设置链接异常的重复次数
		//initRetryHandler(true,2);
	}
	
	@SuppressWarnings("unused")
	private static void initRetryHandler(boolean open,final int retryNumber){
		if(!open){
			return ;
		}
		HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {
			public boolean retryRequest(IOException exception,int executionCount,HttpContext context) {
				if (executionCount >= retryNumber) {
					// 如果超过最大重试次数，那么就不要继续了
					return false;
				}
				if (exception instanceof NoHttpResponseException) {
					// 如果服务器丢掉了连接，那么就重试
					return true;
				}
				if (exception instanceof SSLHandshakeException) {
					// 不要重试SSL握手异常
					return false;
				}
				
				HttpRequest request = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
				if (!(request instanceof HttpEntityEnclosingRequest)) {
					//如果是连接成功但是读取超时的可能存在服务已经接收到请求的可能
					if(exception instanceof  SocketTimeoutException){
						return false;
					}
					// 如果请求被认为是幂等的，那么就重试
					return true;
				}
				return false;
			}
		};
		
		HTTPCLIENT.setHttpRequestRetryHandler(retryHandler);
		
	}
	/**
	 * 增加cookies信息
	 * @param cookies
	 */
	public void addCookies(Map<String,Cookie> cookies){
		
		CookieStore cookieStores = new BasicCookieStore();
		if(cookies != null && cookies.size() > 0) {
			for(String key:cookies.keySet()) {
				cookieStores.addCookie(cookies.get(key));
			}
		}
		localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStores);
	}
	
	public HttpResult post(String url, List<NameValuePair> params) {
		return httpExe(getHttpPost(url, params));
	}

	public HttpResult post(String url, String content) {
		return httpExe(getHttpPost(url, content));
	}
	
	protected HttpPost getHttpPost(String url, List<NameValuePair> params)  {
		HttpPost post = new HttpPost(url);
		try {
			post.setEntity(new UrlEncodedFormEntity(params, CHARSET));
		} catch (UnsupportedEncodingException e) {
			log.error(e);
			
		}
		return post;
		
	}

	protected HttpPost getHttpPost(String url, String content) {
		HttpPost post = new HttpPost(url);
		try {
			post.setEntity(new StringEntity(content, CHARSET));
		} catch (UnsupportedEncodingException e) {
			log.error(e);
			
		}
		return post;
		
	}
	
	public HttpResult get(String url, List<NameValuePair> params) {
		return httpExe(getHttpGet(url, params));
	}

	protected HttpGet getHttpGet(String url, List<NameValuePair> params) {
		String paramStr = URLEncodedUtils.format(params, CHARSET);
		StringBuffer buffer = new StringBuffer(url);
		if (StringUtils.containsNone(url, "?")) {
			buffer.append("?");
		}
		buffer.append(paramStr);
		return new HttpGet(buffer.toString());
	}
	private void setTimeOut(HttpUriRequest request) {
		request.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, socketTimeOut);
		request.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeOut);
	}
	public HttpResult httpExe(HttpUriRequest request) {
		HttpEntity entity = null;
		HttpResult result = new HttpResult();
		try {
			
			setRequestHeaders(request);
			setTimeOut(request);
			process(request,localContext);
			HttpResponse response = HTTPCLIENT.execute(request,localContext);
			
			StatusLine status = response.getStatusLine();
			entity = response.getEntity();
			result.setHeaders(response.getAllHeaders());
			result.setStatusCode(status.getStatusCode());
			
			if (status != null && status.getStatusCode() == OK) {
				result.setContent(EntityUtils.toString(entity));
			} else {
				result.setContent("");
			}
		} catch (SocketTimeoutException e) {
//			result.setStatusCode(STATUS_CODE_TIMEOUT);
			log.error(e);
		} catch (ConnectTimeoutException e) {
//			result.setStatusCode(STATUS_CODE_TIMEOUT);
			log.error(e);
		} catch (Exception e) {
//			result.setStatusCode(STATUS_CODE_OTHER);
			log.error(e);
		} finally {
			if (request != null) {
				request.abort();
			}

		}

		return result;
	}
	public List<Header> getHeaders() {
		return headers;
	}
	public void setHeaders(List<Header> headers) {
		this.headers = headers;
	}
	protected void setRequestHeaders(HttpUriRequest request) {
		if(this.headers!=null){
			for (Header header : headers) {
				if (header != null) {
					request.addHeader(header);
				}
			}
		}
	}
	/**
	 * 如果在请求前需要特殊操作可以重写此方法
	 * @param request
	 * @param localContext
	 */
	protected void process(HttpUriRequest request,HttpContext localContext){
		
	}
	
}
